#include <stdlib.h>

#include "lib.h"

#include "slotguts.h"
#include "gbadguts.h"
#include "slotspin.h"
#include "getrand.h"
#include "log.h"





static int32 g_lossMeter[] = 
{
    0,
    0,
    0,
    0,
    1       , // 4
    1       , // 5
    2       , // 6
    4       , // 7
    6       , // 8
    10      , // 9
    20      , // 10
    35      , // 11
    55      , // 12
    80      , // 13
    110     , // 14
    150     , // 15
    200     , // 16
    260     , // 17
    325     , // 18
    400     , // 19
    700     , // 20
    1000    , // 21
    1500    , // 22
    2500    , // 23
    3500    , // 24
    5000    , // 25
    7000    , // 26
    9000    , // 27
    11000   , // 28
    1,      // Jackpot
};




enum WheelContents {
    BLANK,
    CHERRY,
    BAR1,
    BAR2,
    BAR3,
    SEVEN,
    JACKPOT,
};



enum SpinResult
{
	EVT_LOSER = 0,
	EVT_1CHERRY ,
	EVT_2CHERRIES,
	EVT_3CHERRIES,
	EVT_ANY3BAR,
	EVT_3BAR1,
	EVT_3BAR2,
	EVT_3BAR3,
	EVT_3SEVENS,
	EVT_JACKPOT,
	NUMEVENTS
};

bool8
GoodBadSlotGuts::isValidBet(uint8 bet)
{
    bool8 rc = SlotGuts::isValidBet(bet);

    if ( !rc )
    {
        /**
         * invalid bet becomes valid if it is a 0 bet and
         * we are in some sort of subgame mode.
         */
        rc = bet == 0 && 
            (m_subGame->getData(SGIDX_FREESPINMODE) || m_subGame->getData(SGIDX_JACKPOTMODE));
    }
    else
    {
        /**
         * A valid bet becomes an invalid bet if it is zero, and
         * we are not in some sort of subgame mode.
         */
		if(bet!=0)
		{
			rc = !((m_subGame->getData(SGIDX_FREESPINMODE) || m_subGame->getData(SGIDX_JACKPOTMODE)));
		}
    }

    return rc;
}


SlotSpin *
GoodBadSlotGuts::makeJackpot()
{
    uint8 *stops = new uint8[getReelCount()];
    SlotSpin *spin = new SlotSpin();

    spin->setBet(0);
    spin->setBonusGameType(0);

    // FogBugz 743 - GChapman
    // When we create the new spin we forgot to 
    // add a valid payline.  This prevented classifyResults()
    // from working.
    spin->addLine(1);

    /**
     * Hard set the reels to jackpots.  The assumption is
     * that the jackpot icon is the second to last icon on
     * the strip (the last being a blank).
     */
    for (uint8 i = 0; i < getReelCount(); i++)
    {
        stops[i] = getIcon(i, getIconCount(i) - 2)->position;
    }

    spin->setStops(getReelCount(), stops);

    classifyResult(spin);

    m_spins.push_back(spin);

    // FogBugz 743 - GChapman
    // When we create the new spin we forgot to 
    // set the spin complete flag.  This prevents the 
    // spin from getting packed and shipped to the client.
    spin->setSpinComplete(true);    

    delete [] stops;

    return spin;
}

void
GoodBadSlotGuts::spin(SlotSpin* slotSpin)
{
 
    if ( m_subGame->getData(SGIDX_JACKPOTMODE) )
    {
        /**
         * If we are in jackpot mode then give a jackpot.
         */
		delete slotSpin;
        slotSpin = makeJackpot();
        m_subGame->update(SGIDX_JACKPOTMODE, 0);
    }
    else if ( m_subGame->getData(SGIDX_FREESPINMODE) )
    {
        if ( m_subGame->getData(SGIDX_FREESPINCOUNT) >= m_subGame->getData(SGIDX_SPINCOUNTREQUEST) &&
             m_subGame->getData(SGIDX_SPINCOUNTREQUEST) > 0 )
        {
            /**
             * Free spin mode.
             */
            do
            {
				slotSpin = new SlotSpin();

				slotSpin->setBet(0);
				slotSpin->setBonusGameType(0);
				slotSpin->addLine(1);

				makeWinningSpin(slotSpin);

//                slotSpin = makeWinningSpin(slotSpin->getBet(), slotSpin->getBonusGame());
                m_subGame->update(SGIDX_FREESPINCOUNT, m_subGame->getData(SGIDX_FREESPINCOUNT) - 1);
                m_subGame->update(SGIDX_FREESPINMODE, m_subGame->getData(SGIDX_FREESPINCOUNT) > 0);
                m_subGame->update(SGIDX_SPINCOUNTREQUEST, m_subGame->getData(SGIDX_SPINCOUNTREQUEST) - 1);
    
            } while ( m_subGame->getData(SGIDX_SPINCOUNTREQUEST) > 0 );
        }
        else
        {
            /**
             * Free spin count is an invalid value.
             */
//            ASSERT(0);
			Log::msgWarning("Good to be bad - Free Spin Count Invalid: %d", m_subGame->getData(SGIDX_SPINCOUNTREQUEST));

 //           m_hardMalfunction = true;
        }
    }
    else
    {
        /**
         * Normal spin mode.
         */

//#define ALLLOSERS
#ifdef ALLLOSERS
		makeLosingSpin(slotSpin);
#else
		SlotGuts::spin(slotSpin);
#endif
        /**
         * Only max bets affect the meter.
         */
        if ( slotSpin->getBet() == this->getBetCount() )
        {
            SpinLine *spinLine = slotSpin->getSpinLineAt(0);
    
            if ( spinLine->payouttypeid == EVT_LOSER )
            {
                m_subGame->update(SGIDX_LOSSES, m_subGame->getData(SGIDX_LOSSES)+1);
                m_subGame->update(SGIDX_FREESPINCOUNT, g_lossMeter[m_subGame->getData(SGIDX_LOSSES)]);
    
                if ( m_subGame->getData(SGIDX_LOSSES) >= SGDEFS_JACKPOTLIMIT )
                {
                    m_subGame->update(SGIDX_JACKPOTMODE, 1);

                    // FogBugz 759 - GChapman
                    // Initalizing subgame data to ensure values are passed to the client correctly.
                    m_subGame->update(SGIDX_FREESPINMODE, 0);
                    m_subGame->update(SGIDX_FREESPINCOUNT, 0);
                }
            }
            else
            {
                m_subGame->update(SGIDX_LOSSES, 0);
                m_subGame->update(SGIDX_FREESPINCOUNT, 0);
                m_subGame->update(SGIDX_SPINCOUNTREQUEST, 0);
            }
        }
    }

#ifdef DEBUGVERBOSE
	DEBUGMSG(("\nLosses: %d", m_subGame->getData(SGIDX_LOSSES)));
	DEBUGMSG(("Free Spins: %d", m_subGame->getData(SGIDX_FREESPINCOUNT)));
	DEBUGMSG(("Free Spin Mode: %d", m_subGame->getData(SGIDX_FREESPINMODE)));
	DEBUGMSG(("Jackpot Mode: %d", m_subGame->getData(SGIDX_JACKPOTMODE)));
	DEBUGMSG(("Spins Requested: %d\n", m_subGame->getData(SGIDX_SPINCOUNTREQUEST)));
#endif

	setGameComplete(true);
}


void 
GoodBadSlotGuts::makeWinningSpin(SlotSpin *spin)
{
    uint8 *stops = new uint8[getReelCount()];
	bool8 hadCherry = false;
	bool8 respin = true;

    /**
     * Spin until we get a winner (we throw away the first cherry
     */
    while (respin)
    {
        for ( uint8 i = 0; i < getReelCount() ; i++)
        {
            stops[i] = this->findPositionByWeight(getrand(), i);
        }
        spin->setStops(getReelCount(), stops);
        classifyResult(spin);
		if ( !hadCherry && spin->getSpinLineByLineId(1)->payouttypeid == EVT_1CHERRY )
		{
            hadCherry = true;
		}
		else
		{
			if(spin->getTotalPayoutAmount() > 0)
			{
				respin = false;
			}
		}
    }

    spin->setSpinComplete(true);
    m_spins.push_back(spin);

#ifdef DEBUGVERBOSE
	DEBUGMSG(("MakeWinningSpin - Spin: bet(%d) payout(%d) jackpot(%d)",
              spin->getTotalBet(), spin->getTotalPayoutAmount() / m_info->creditValue, spin->getJackpot()));
    for ( uint8 reelid = 0; reelid < getReelCount(); reelid++ )
    {
        DEBUGMSG(("                     wheel %d: [%s])",
                  reelid, 
                  getIcon(reelid, spin->getStops()[reelid])->iconName));
    }
#endif

    delete [] stops;

}

void 
GoodBadSlotGuts::makeLosingSpin(SlotSpin *spin)
{
    uint8 *stops = new uint8[getReelCount()];
	bool8 respin = true;

    /**
     * Spin until we get a loser 
     */
    while (respin)
    {
        for ( uint8 i = 0; i < getReelCount() ; i++)
        {
            stops[i] = this->findPositionByWeight(getrand(), i);
        }
        spin->setStops(getReelCount(), stops);
        classifyResult(spin);
		if(spin->getTotalPayoutAmount() == 0)
		{
			//loser
				respin = false;
		}
    }

    spin->setSpinComplete(true);

    m_spins.push_back(spin);

	DEBUGMSG(("MakeWinningSpin - Spin: bet(%d) payout(%d) jackpot(%d)",
              spin->getTotalBet(), spin->getTotalPayoutAmount() / m_info->creditValue, spin->getJackpot()));
    for ( uint8 reelid = 0; reelid < getReelCount(); reelid++ )
    {
        DEBUGMSG(("                     wheel %d: [%s])",
                  reelid, 
                  getIcon(reelid, spin->getStops()[reelid])->iconName));
    }

    delete [] stops;

}



bool8 
GoodBadSlotGuts::isValidGuts()
{
    return getGutsId() > 700 && getGutsId() < 800;
}


uint8
GoodBadSlotGuts::classifyStops(uint8* stops,uint8 bet, uint8 line)
{
	uint8 result = EVT_LOSER;
    int numCherries = 0;
    int numSevens = 0;
    int numBar1 = 0;
    int numBar2 = 0;
    int numBar3 = 0;
    int numBars = 0;
    int numJackpots = 0;

    //  Do a preliminary run-through
    for ( uint8 i = 0; i < getReelCount(); i++ )
    {
        // Set the icon flags;
        switch ( getIcon(i, stops[i])->iconid )
        {
            case CHERRY:
                numCherries++;
                break;

            case SEVEN:
                numSevens++;
                break;

            case BAR1:
                numBar1++;
                numBars++;
                break;

            case BAR2:
                numBar2++;
                numBars++;
                break;

            case BAR3:
                numBar3++;
                numBars++;
                break;

            case JACKPOT:
                numJackpots++;
                break;
        }
    }


    //  EVT_JACKPOT ** Jackpot **
    if ( numJackpots == 3 )
    {
        result = EVT_JACKPOT;
    }

    //  EVT_3SEVENS ** Small Jackpot **
    if ( result == EVT_LOSER && numSevens == 3 )
    {
        result = EVT_3SEVENS;
    }

    // EVT_3BAR1
    if ( result == EVT_LOSER && numBar1 ==3 )
    {
        result = EVT_3BAR1;
    }

    // EVT_3BAR2
    if ( result == EVT_LOSER && numBar2 == 3 )
    {
        result = EVT_3BAR2;
    }

    // EVT_3BAR3
    if ( result == EVT_LOSER && numBar3 == 3 )
    {
        result = EVT_3BAR3;
    }

    // EVT_ANY3BARS     -  Must be checked after the bar triplets
    if ( result == EVT_LOSER && numBars == 3 )
    {
        result = EVT_ANY3BAR;
    }

    // EVT_1CHERRY
    // EVT_2CHERRIES
    // EVT_3CHERRIES
    //
    if ( result == EVT_LOSER && numCherries > 0 )
    {
        if ( numCherries == 3 )
        {
            result = EVT_3CHERRIES;
        }
        else if ( numCherries == 2 )
        {
            result = EVT_2CHERRIES;
        }
        else
        {
            result = EVT_1CHERRY;
        }
    }


	return result;
}



void 
GoodBadSlotGuts::playBonusGame(uint8 bonusGameType, int32 bonusDataCount, int32 *bonusData)
{
    m_subGame->update(SGIDX_SPINCOUNTREQUEST, 0);

    for ( int32 i = 0; i < bonusDataCount; i++ )
    {
        m_subGame->update(SGIDX_SPINCOUNTREQUEST, bonusData[i]);
    }


    if ( m_subGame->getData(SGIDX_SPINCOUNTREQUEST) == 0 || m_subGame->getData(SGIDX_SPINCOUNTREQUEST) > m_subGame->getData(SGIDX_FREESPINCOUNT) )
    {
        /**
         * Invalid spin count received.
         */
		Log::msgWarning("Good to be bad - Invalid Spin Count: %d", m_subGame->getData(SGIDX_SPINCOUNTREQUEST));

    }
    else
    {
        if ( m_subGame->getData(SGIDX_LOSSES) >= SGDEFS_JACKPOTLIMIT )
        {
            /**
             * Convert to jackpot mode
             */
            m_subGame->update(SGIDX_JACKPOTMODE, 1);
            m_subGame->update(SGIDX_FREESPINMODE, 0);
            m_subGame->update(SGIDX_LOSSES, 0);
        }
        else if ( m_subGame->getData(SGIDX_FREESPINCOUNT) > 0 )
        {
            /* Turn on free spin mode */
            m_subGame->update(SGIDX_JACKPOTMODE, 0);
            m_subGame->update(SGIDX_FREESPINMODE, 1);
            m_subGame->update(SGIDX_LOSSES, 0);
        }
        else
        {
            /**
             * Uknown reason for bonus game message.
             */
//            ASSERT(0);
			Log::msgWarning("Good to be bad - Very bad bonus game message.");
 //            m_hardMalfunction = true;
        }
    }

    
}


